#include "weatherclient.h"
#include <QUrlQuery>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QTimer>
#include <QDateTime>
#include <QSslError>
#include <QSslConfiguration>

WeatherClient::WeatherClient(QObject* parent) : QObject(parent) {
    qRegisterMetaType<WeatherData>("WeatherData");
    qRegisterMetaType<DailyItem>("DailyItem");
}

QUrl WeatherClient::weatherUrl(double lat, double lon) const {
    // 只取需要的字段：无降水、无小图标
    QUrl u("https://api.open-meteo.com/v1/forecast");
    QUrlQuery q;
    q.addQueryItem("latitude",  QString::number(lat, 'f', 4));
    q.addQueryItem("longitude", QString::number(lon, 'f', 4));
    q.addQueryItem("current",   "temperature_2m,relative_humidity_2m,weather_code,wind_speed_10m,wind_direction_10m");
    q.addQueryItem("daily",     "weather_code,temperature_2m_max,temperature_2m_min,wind_speed_10m_max,wind_direction_10m_dominant");
    q.addQueryItem("forecast_days", "3");
    q.addQueryItem("timezone",  "Asia/Shanghai"); // 或 "auto"
    u.setQuery(q);
    return u;
}

void WeatherClient::fetchByCoordinates(double lat, double lon,
                                       const QString& cityLabel, const QString& lang) {
    if (m_replyWx) { m_replyWx->abort(); m_replyWx = nullptr; }
    m_lang = lang;

    QNetworkRequest req(weatherUrl(lat, lon));

    // 若仍在排障，可临时关闭校验（生产请注释掉）：
     QSslConfiguration conf = QSslConfiguration::defaultConfiguration();
     conf.setPeerVerifyMode(QSslSocket::VerifyNone);
     conf.setProtocol(QSsl::TlsV1_2OrLater);
     req.setSslConfiguration(conf);

    m_replyWx = m_mgr.get(req);
    m_replyWx->setProperty("wx_city_name", cityLabel);
    m_replyWx->setProperty("wx_lat",  lat);
    m_replyWx->setProperty("wx_lon",  lon);

    QTimer::singleShot(m_timeoutMs, m_replyWx, [r=m_replyWx](){
        if (r && r->isRunning()) r->abort();
    });

    connect(m_replyWx, &QNetworkReply::finished, this, &WeatherClient::handleWeatherFinished);
    attachCommonErrorHandlers(m_replyWx);
}

void WeatherClient::handleWeatherFinished() {
    if (!m_replyWx) return;
    const int http = m_replyWx->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();

    if (m_replyWx->error() != QNetworkReply::NoError) {
        emit error(QString("Weather request failed: %1").arg(m_replyWx->errorString()), http);
        m_replyWx->deleteLater(); m_replyWx = nullptr;
        return;
    }

    const QByteArray raw = m_replyWx->readAll();

    WeatherData d;
    d.city      = m_replyWx->property("wx_city_name").toString();
    d.latitude  = m_replyWx->property("wx_lat").toDouble();
    d.longitude = m_replyWx->property("wx_lon").toDouble();

    m_replyWx->deleteLater(); m_replyWx = nullptr;

    QJsonParseError pe{}; auto doc = QJsonDocument::fromJson(raw, &pe);
    if (pe.error != QJsonParseError::NoError || !doc.isObject()) {
        emit error(QString("Weather JSON parse error: %1").arg(pe.errorString()), http);
        return;
    }
    const QJsonObject obj = doc.object();

    // 实况
    if (obj.contains("current") && obj.value("current").isObject()) {
        const QJsonObject cur = obj.value("current").toObject();
        d.temperature    = cur.value("temperature_2m").toDouble();
        d.humidity       = cur.value("relative_humidity_2m").toInt(-1);
        d.windSpeed      = cur.value("wind_speed_10m").toDouble();
        d.windDirection  = cur.value("wind_direction_10m").toInt(-1);
        d.weatherCode    = cur.value("weather_code").toInt(-1);

        QDateTime dtLocal = QDateTime::currentDateTime();
        if (cur.contains("time") && cur.value("time").isString()) {
            const QString iso = cur.value("time").toString();
            QDateTime dt = QDateTime::fromString(iso, Qt::ISODate);
            if (dt.isValid()) dtLocal = dt;
        }
        d.updatedAt = dtLocal.toString("yyyy-MM-dd HH:mm:ss");
        d.weekday   = weekdayZh(dtLocal);
    } else if (obj.contains("current_weather") && obj.value("current_weather").isObject()) {
        const QJsonObject cur = obj.value("current_weather").toObject();
        d.temperature   = cur.value("temperature").toDouble();
        d.windSpeed     = cur.value("windspeed").toDouble();
        d.weatherCode   = cur.value("weathercode").toInt(-1);
        d.humidity      = -1;
        d.windDirection = cur.value("winddirection").toInt(-1);
        QDateTime now = QDateTime::currentDateTime();
        d.updatedAt = now.toString("yyyy-MM-dd HH:mm:ss");
        d.weekday   = weekdayZh(now);
    } else {
        emit error("No current weather in response", http);
        return;
    }

    if (d.city.isEmpty()) d.city = QStringLiteral("成都");
    d.weatherText       = codeToText(d.weatherCode);
    d.windDirectionText = (d.windDirection >= 0 ? degToDirZh(d.windDirection) : QStringLiteral("—"));

    // 3 日预报（无降水）
    if (obj.contains("daily") && obj.value("daily").isObject()) {
        const QJsonObject day = obj.value("daily").toObject();
        const auto A_time   = day.value("time").toArray();
        const auto A_code   = day.value("weather_code").toArray();
        const auto A_tmax   = day.value("temperature_2m_max").toArray();
        const auto A_tmin   = day.value("temperature_2m_min").toArray();
        const auto A_wmax   = day.value("wind_speed_10m_max").toArray();
        const auto A_wdir   = day.value("wind_direction_10m_dominant").toArray();

        const int n = qMin(3, A_time.size());
        d.daily.reserve(n);
        for (int i=0; i<n; ++i) {
            DailyItem it;
            it.date        = A_time.at(i).toString();
            const QDate qd = QDate::fromString(it.date, Qt::ISODate);
            if (qd.isValid()) it.weekday = weekdayZh(qd);

            it.weatherCode = (i < A_code.size()) ? A_code.at(i).toInt(-1) : -1;
            it.weatherText = codeToText(it.weatherCode);
            it.tMax        = (i < A_tmax.size()) ? A_tmax.at(i).toDouble() : 0;
            it.tMin        = (i < A_tmin.size()) ? A_tmin.at(i).toDouble() : 0;
            it.windSpeedMax= (i < A_wmax.size()) ? A_wmax.at(i).toDouble() : 0;
            it.windDirDominant = (i < A_wdir.size()) ? A_wdir.at(i).toInt(-1) : -1;
            it.windDirText = (it.windDirDominant >= 0 ? degToDirZh(it.windDirDominant) : QStringLiteral("—"));

            d.daily.push_back(it);
        }
    }

    emit gotWeather(d);
}

QString WeatherClient::codeToText(int c) {
    switch (c) {
    case 0:                  return QStringLiteral("晴");
    case 1:
    case 2:
    case 3:                  return QStringLiteral("多云");

    // 雨（Open-Meteo 语义：61/63/65 = 小/中/大雨）
    case 61:                 return QStringLiteral("小雨");
    case 63:                 return QStringLiteral("中雨");
    case 65:                 return QStringLiteral("大雨");

    // 雪（71/73/75 = 小/中/大雪）
    case 71:                 return QStringLiteral("小雪");
    case 73:                 return QStringLiteral("中雪");
    case 75:                 return QStringLiteral("大雪");

    // 雷阵雨（95/96/99 = 含雷暴；统一用一张“雷阵雨”图）
    case 95:
    case 96:
    case 99:                 return QStringLiteral("雷阵雨");

    default:                 return QStringLiteral("多云"); // 其他全部收敛到“多云”
    }
}

QString WeatherClient::degToDirZh(int deg) {
    static const char* k[8] = {"北","东北","东","东南","南","西南","西","西北"};
    int d = deg % 360; if (d < 0) d += 360;
    int idx = int((d + 22.5) / 45.0) % 8;
    return QString::fromUtf8(k[idx]);
}

QString WeatherClient::weekdayZh(const QDateTime& dt) {
    switch (dt.date().dayOfWeek()) {
    case 1: return QStringLiteral("周一");
    case 2: return QStringLiteral("周二");
    case 3: return QStringLiteral("周三");
    case 4: return QStringLiteral("周四");
    case 5: return QStringLiteral("周五");
    case 6: return QStringLiteral("周六");
    case 7: return QStringLiteral("周日");
    default: return QStringLiteral("周?");
    }
}

void WeatherClient::attachCommonErrorHandlers(QNetworkReply* r) {
#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
    connect(r, &QNetworkReply::errorOccurred, this, [this, r](QNetworkReply::NetworkError){
        const int code = r->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
        emit error(r->errorString(), code);
    });
#else
    connect(r, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, [this, r](QNetworkReply::NetworkError){
        const int code = r->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
        emit error(r->errorString(), code);
    });
#endif
    connect(r, &QNetworkReply::sslErrors, this, [this, r](const QList<QSslError>& errs){
        QStringList msgs; for (const auto& e : errs) msgs << e.errorString();
        const int code = r->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
        emit error(QString("SSL errors: %1").arg(msgs.join("; ")), code);
    });
}


